package com.gongfutrip.trans.client.common;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gongfutrip.trans.remoting.protocol.RemotingCommand;

public class ClientUtils {
  private static final Logger logger = LoggerFactory.getLogger(ClientUtils.class);

  public static final char NAME_VALUE_SEPARATOR = 1;
  public static final char PROPERTY_SEPARATOR = 2;
  private final static int compressMsgBodyOverHowmuch = 1024 * 4;// 4K
  public static final int COMPRESSED_FLAG = 0x1;

  public static int getCompressMsgBodyOverHowmuch() {
    return compressMsgBodyOverHowmuch;
  }


  public static boolean tryToCompressMessage(final Message msg) {
    byte[] body = msg.getBody();
    if (body != null) {
      if (body.length >= ClientUtils.getCompressMsgBodyOverHowmuch()) {
        try {
          byte[] data = ClientUtils.compress(body, 5);
          if (data != null) {
            msg.setBody(data);
            return true;
          }
        } catch (IOException e) {
          logger.error("tryToCompressMessage exception", e);
          logger.warn(msg.toString());
        }
      }
    }
    return false;
  }

  public static boolean tryToCompressMessage(final RemotingCommand resopnse) {
    byte[] body = resopnse.getBody();
    if (body != null) {
      if (body.length >= ClientUtils.getCompressMsgBodyOverHowmuch()) {
        try {
          byte[] data = ClientUtils.compress(body, 5);
          if (data != null) {
            resopnse.setBody(data);
            return true;
          }
        } catch (IOException e) {
          logger.error("tryToCompressMessage exception", e);
          logger.warn(resopnse.toString());
        }
      }
    }
    return false;
  }

  public static String messageProperties2String(Map<String, String> properties) {
    StringBuilder sb = new StringBuilder();
    if (properties != null) {
      for (final Map.Entry<String, String> entry : properties.entrySet()) {
        final String name = entry.getKey();
        final String value = entry.getValue();

        sb.append(name);
        sb.append(NAME_VALUE_SEPARATOR);
        sb.append(value);
        sb.append(PROPERTY_SEPARATOR);
      }
    }
    return sb.toString();
  }



  public static Map<String, String> string2messageProperties(final String properties) {
    Map<String, String> map = new HashMap<String, String>();
    if (properties != null) {
      String[] items = properties.split(String.valueOf(PROPERTY_SEPARATOR));
      for (String i : items) {
        String[] nv = i.split(String.valueOf(NAME_VALUE_SEPARATOR));
        if (2 == nv.length) {
          map.put(nv[0], nv[1]);
        }
      }
    }
    return map;
  }

  public static byte[] compress(final byte[] src, final int level) throws IOException {
    byte[] result = src;
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(src.length);
    java.util.zip.Deflater defeater = new java.util.zip.Deflater(level);
    DeflaterOutputStream deflaterOutputStream =
        new DeflaterOutputStream(byteArrayOutputStream, defeater);
    try {
      deflaterOutputStream.write(src);
      deflaterOutputStream.finish();
      deflaterOutputStream.close();
      result = byteArrayOutputStream.toByteArray();
    } catch (IOException e) {
      defeater.end();
      throw e;
    } finally {
      try {
        byteArrayOutputStream.close();
      } catch (IOException ignored) {
      }

      defeater.end();
    }

    return result;
  }

  public static byte[] uncompress(final byte[] src) throws IOException {
    byte[] result = src;
    byte[] uncompressData = new byte[src.length];
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(src);
    InflaterInputStream inflaterInputStream = new InflaterInputStream(byteArrayInputStream);
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(src.length);

    try {
      while (true) {
        int len = inflaterInputStream.read(uncompressData, 0, uncompressData.length);
        if (len <= 0) {
          break;
        }
        byteArrayOutputStream.write(uncompressData, 0, len);
      }
      byteArrayOutputStream.flush();
      result = byteArrayOutputStream.toByteArray();
    } catch (IOException e) {
      throw e;
    } finally {
      try {
        byteArrayInputStream.close();
      } catch (IOException ignored) {
      }
      try {
        inflaterInputStream.close();
      } catch (IOException ignored) {
      }
      try {
        byteArrayOutputStream.close();
      } catch (IOException ignored) {
      }
    }

    return result;
  }
}
